{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 如何使用jieba結巴中文分詞"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![tag-cloud](https://ithelp.ithome.com.tw/upload/images/20171219/20107576LXAdzuZue5.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note: 本文主要修改與擷取了[林志傑-如何使用 JIEBA 結巴中文分詞程式](http://blog.fukuball.com/ru-he-shi-yong-jieba-jie-ba-zhong-wen-fen-ci-cheng-shi/)的內容。\n",
    "文字雲的圖像來源為[GoatWang-2018 iT 邦幫忙鐵人賽](https://ithelp.ithome.com.tw/articles/10192043?sc=rss.qu)。\n",
    "\n",
    "對中文的斷詞有興趣強烈建議可以參考:[中文斷詞：斷句不要悲劇](https://speakerdeck.com/fukuball/head-first-chinese-text-segmentation)\n",
    "\n",
    "本文主要是為了後續介詔word2vec的理論與實作的前置準備。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "自然語言處理(Natural Language Processing)的其中一個重要環節就是中文斷詞的處理，比起英文斷詞，中文斷詞在先天上就比較難處理，比如電腦要怎麼知道「全台大停電」要斷詞成「全台 / 大 / 停電」呢？如果是英文「Power outage all over Taiwan」，就可以直接用空白字元來斷成「Power / outage / all / over / Taiwan」，可見中文斷詞真的是一個大問題啊～!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[结巴 (jieba) 中文分詞](https://github.com/fxsjy/jieba)是很好很簡單使用的Python中文分詞函式庫。\n",
    "它有以下的特色:\n",
    "* 支持三種分詞模式：\n",
    "    * 精確模式，試圖將句子最精確地切開，適合文本分析。\n",
    "    * 全模式，把句子中所有的可以成詞的詞語都掃描出來，速度非常快，但是不能解決歧義。\n",
    "    * 搜索引擎模式，在精確模式的基礎上，對長詞再次切分，提高召回率，適合用於搜索引擎分詞。\n",
    "* 支持繁體分詞\n",
    "* 支持自定義詞典\n",
    "* MIT授權協議\n",
    "\n",
    "jieba中文斷詞所使用的演算法是基於 Trie Tree 結構去生成句子中中文字所有可能成詞的情況，然後使用動態規劃（Dynamic programming）算法來找出最大機率的路徑，這個路徑就是基於詞頻的最大斷詞結果。對於辨識新詞（字典詞庫中不存在的詞）則使用了 HMM 模型（Hidden Markov Model）及 Viterbi 算法來辨識出來。\n",
    "\n",
    "在[中文斷詞-by Mark Chang](https://www.slideshare.net/ckmarkohchang/chinese-words-segmentation-tutorial)的分享裡有詳細的解說。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 安裝 jieba\n",
    "\n",
    "方法#1\n",
    "`pip install jieba`\n",
    "\n",
    "方法#2\n",
    "`conda install -c conda-forge jieba`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 基本斷詞用法，使用預設詞庫\n",
    "\n",
    "三種分詞模式示範：\n",
    "* 精確模式，試圖將句子最精確地切開，適合文本分析。\n",
    "* 全模式，把句子中所有的可以成詞的詞語都掃描出來，速度非常快，但是不能解決歧義。\n",
    "* 搜索引擎模式，在精確模式的基礎上，對長詞再次切分，提高召回率，適合用於搜索引擎分詞。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Building prefix dict from the default dictionary ...\n",
      "Loading model from cache /var/folders/vt/rfsxsh1j2b51dffzl9p5v72ngb1v7q/T/jieba.cache\n",
      "Loading model cost 0.929 seconds.\n",
      "Prefix dict has been built succesfully.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "全模式: 我/ 來/ 到/ 新竹/ 清/ 華/ 大/ 學\n",
      "精確模式: 我來/ 到/ 新竹/ 清華大學\n",
      "精確模式(預設): 他來/ 到/ 了/ 台北/ 101/ 大樓\n",
      "搜索引擎模式模式: Apache/  / Spark/ 是/ 一個/ 開源/ 叢集/ 運算/ 框架/ ，/ 最初/ 是/ 由/ 加州/ 大學柏克萊/ 分校/ AMPLab/ 所開發/ 。\n"
     ]
    }
   ],
   "source": [
    "# encoding=utf-8\n",
    "import jieba\n",
    "\n",
    "seg_list = jieba.cut(\"我來到新竹清華大學\", cut_all=True)\n",
    "print(\"全模式: \" + \"/ \".join(seg_list))  # 全模式\n",
    "\n",
    "seg_list = jieba.cut(\"我來到新竹清華大學\", cut_all=False)\n",
    "print(\"精確模式: \" + \"/ \".join(seg_list))  # 精確模式\n",
    "\n",
    "seg_list = jieba.cut(\"他來到了台北101大樓\")  # 預設為精確模式\n",
    "print(\"精確模式(預設): \" +\"/ \".join(seg_list))\n",
    "\n",
    "seg_list = jieba.cut_for_search(\"Apache Spark是一個開源叢集運算框架，最初是由加州大學柏克萊分校AMPLab所開發。\")  # 搜索引擎模式\n",
    "print(\"搜索引擎模式模式: \" +\"/ \".join(seg_list))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "據原作者的說法，使用預設詞庫的話，繁體中文的斷詞結果應該會比較差，畢竟原來的詞庫是簡體中文。我們接下來試試看中文歌詞的斷詞結果如何。\n",
    "\n",
    "現在我們使用[回聲樂團 - 座右銘](http://www.indievox.com/song/1)的歌詞作為中文斷詞測試範例，歌詞我們先做成一個純文字檔lyric.txt置放到跟這個jupyter notebook相同的目錄下。\n",
    "\n",
    "內容如下：\n",
    "\n",
    ">我沒有心\n",
    "我沒有真實的自我\n",
    "我只有消瘦的臉孔\n",
    "所謂軟弱\n",
    "所謂的順從一向是我\n",
    "的座右銘\n",
    "\n",
    ">而我\n",
    "沒有那海洋的寬闊\n",
    "我只要熱情的撫摸\n",
    "所謂空洞\n",
    "所謂不安全感是我\n",
    "的墓誌銘\n",
    "\n",
    ">而你\n",
    "是否和我一般怯懦\n",
    "是否和我一般矯作\n",
    "和我一般囉唆\n",
    "\n",
    ">而你\n",
    "是否和我一般退縮\n",
    "是否和我一般肌迫\n",
    "一般地困惑\n",
    "\n",
    ">我沒有力\n",
    "我沒有滿腔的熱火\n",
    "我只有滿肚的如果\n",
    "所謂勇氣\n",
    "所謂的認同感是我\n",
    "隨便說說\n",
    "\n",
    ">而你\n",
    "是否和我一般怯懦\n",
    "是否和我一般矯作\n",
    "是否對你來說\n",
    "只是一場遊戲\n",
    "雖然沒有把握\n",
    "\n",
    ">而你\n",
    "是否和我一般退縮\n",
    "是否和我一般肌迫\n",
    "是否對你來說\n",
    "只是逼不得已\n",
    "雖然沒有藉口"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "精確模式: 我/ 沒/ 有心/ \n",
      "/ 我/ 沒/ 有/ 真實/ 的/ 自我/ \n",
      "/ 我/ 只有/ 消瘦/ 的/ 臉孔/ \n",
      "/ 所謂/ 軟弱/ \n",
      "/ 所謂/ 的/ 順/ 從/ 一向/ 是/ 我/ \n",
      "/ 的/ 座/ 右銘/ \n",
      "/ \n",
      "/ 而/ 我/ \n",
      "/ 沒有/ 那/ 海洋/ 的/ 寬闊/ \n",
      "/ 我/ 只要/ 熱情/ 的/ 撫/ 摸/ \n",
      "/ 所謂/ 空洞/ \n",
      "/ 所謂/ 不安全感/ 是/ 我/ \n",
      "/ 的/ 墓誌/ 銘/ \n",
      "/ \n",
      "/ 而/ 你/ \n",
      "/ 是否/ 和/ 我/ 一般/ 怯懦/ \n",
      "/ 是否/ 和/ 我/ 一般/ 矯作/ \n",
      "/ 和/ 我/ 一般/ 囉/ 唆/ \n",
      "/ \n",
      "/ 而/ 你/ \n",
      "/ 是否/ 和/ 我/ 一般/ 退縮/ \n",
      "/ 是否/ 和/ 我/ 一般/ 肌迫/ \n",
      "/ 一般/ 地/ 困惑/ \n",
      "/ \n",
      "/ 我/ 沒/ 有力/ \n",
      "/ 我/ 沒/ 有/ 滿腔/ 的/ 熱火/ \n",
      "/ 我/ 只有/ 滿肚/ 的/ 如果/ \n",
      "/ 所謂/ 勇氣/ \n",
      "/ 所謂/ 的/ 認/ 同感/ 是/ 我/ \n",
      "/ 隨便/ 說/ 說/ \n",
      "/ \n",
      "/ 而/ 你/ \n",
      "/ 是否/ 和/ 我/ 一般/ 怯懦/ \n",
      "/ 是否/ 和/ 我/ 一般/ 矯作/ \n",
      "/ 是否/ 對/ 你/ 來/ 說/ \n",
      "/ 只是/ 一場/ 遊戲/ \n",
      "/ 雖然/ 沒/ 有把握/ \n",
      "/ \n",
      "/ 而/ 你/ \n",
      "/ 是否/ 和/ 我/ 一般/ 退縮/ \n",
      "/ 是否/ 和/ 我/ 一般/ 肌迫/ \n",
      "/ 是否/ 對/ 你/ 來/ 說/ \n",
      "/ 只是/ 逼不得已/ \n",
      "/ 雖然/ 沒有/ 藉口/ \n",
      "\n"
     ]
    }
   ],
   "source": [
    "import jieba\n",
    "\n",
    "content = open('lyric.txt', 'rb').read()\n",
    "\n",
    "seg_list = jieba.cut(content, cut_all=False)\n",
    "print(\"精確模式: \" + \"/ \".join(seg_list))  # 精確模式"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我們可以從結果看出斷詞已經開始出了一些問題，比如「座右銘」被斷成了「座 / 右銘」，「墓誌銘」被斷成了「墓誌 / 銘」，這應該就是因為預設詞庫是簡體中文所造成，因此繁體中文的斷詞結果會比較差，還好 jieba 也提供了可以切換詞庫的功能，並提供了一個繁體中文詞庫，所以我們可以使用切換詞庫的功能來改善斷詞結果。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 中文歌詞斷詞，使用繁體詞庫\n",
    "\n",
    "從 https://github.com/fxsjy/jieba/blob/master/extra_dict/dict.txt.big  下載繁體詞庫檔，置放到跟這個jupyter notebook相同的目錄下。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Building prefix dict from /Users/8703147/pythonworks/dict.txt.big ...\n",
      "Loading model from cache /var/folders/vt/rfsxsh1j2b51dffzl9p5v72ngb1v7q/T/jieba.u7693eb24db7dff9a9936cf7ca1f4b9b9.cache\n",
      "Loading model cost 1.590 seconds.\n",
      "Prefix dict has been built succesfully.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "精確模式: 我/ 沒有/ 心/ \n",
      "/ 我/ 沒有/ 真實/ 的/ 自我/ \n",
      "/ 我/ 只有/ 消瘦/ 的/ 臉孔/ \n",
      "/ 所謂/ 軟弱/ \n",
      "/ 所謂/ 的/ 順從/ 一向/ 是/ 我/ \n",
      "/ 的/ 座右銘/ \n",
      "/ \n",
      "/ 而/ 我/ \n",
      "/ 沒有/ 那/ 海洋/ 的/ 寬闊/ \n",
      "/ 我/ 只要/ 熱情/ 的/ 撫摸/ \n",
      "/ 所謂/ 空洞/ \n",
      "/ 所謂/ 不安全感/ 是/ 我/ \n",
      "/ 的/ 墓誌銘/ \n",
      "/ \n",
      "/ 而/ 你/ \n",
      "/ 是否/ 和/ 我/ 一般/ 怯懦/ \n",
      "/ 是否/ 和/ 我/ 一般/ 矯作/ \n",
      "/ 和/ 我/ 一般/ 囉唆/ \n",
      "/ \n",
      "/ 而/ 你/ \n",
      "/ 是否/ 和/ 我/ 一般/ 退縮/ \n",
      "/ 是否/ 和/ 我/ 一般/ 肌迫/ \n",
      "/ 一般/ 地/ 困惑/ \n",
      "/ \n",
      "/ 我/ 沒有/ 力/ \n",
      "/ 我/ 沒有/ 滿腔/ 的/ 熱火/ \n",
      "/ 我/ 只有/ 滿肚/ 的/ 如果/ \n",
      "/ 所謂/ 勇氣/ \n",
      "/ 所謂/ 的/ 認同感/ 是/ 我/ \n",
      "/ 隨便說說/ \n",
      "/ \n",
      "/ 而/ 你/ \n",
      "/ 是否/ 和/ 我/ 一般/ 怯懦/ \n",
      "/ 是否/ 和/ 我/ 一般/ 矯作/ \n",
      "/ 是否/ 對/ 你/ 來說/ \n",
      "/ 只是/ 一場/ 遊戲/ \n",
      "/ 雖然/ 沒有/ 把握/ \n",
      "/ \n",
      "/ 而/ 你/ \n",
      "/ 是否/ 和/ 我/ 一般/ 退縮/ \n",
      "/ 是否/ 和/ 我/ 一般/ 肌迫/ \n",
      "/ 是否/ 對/ 你/ 來說/ \n",
      "/ 只是/ 逼不得已/ \n",
      "/ 雖然/ 沒有/ 藉口/ \n",
      "\n"
     ]
    }
   ],
   "source": [
    "import jieba\n",
    "\n",
    "# 指定使用繁體詞庫\n",
    "jieba.set_dictionary('dict.txt.big') \n",
    "\n",
    "content = open('lyric.txt', 'rb').read()\n",
    "\n",
    "seg_list = jieba.cut(content, cut_all=False)\n",
    "print(\"精確模式: \" + \"/ \".join(seg_list))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我們在程式中多加一行 jieba.set_dictionary('dict.txt.big')，這樣就可以將斷詞詞庫切換到 dic.txt.big 這個檔案。\n",
    "\n",
    "得到的斷詞結果會是：\n",
    ">我 / 沒有 / 心 / 我 / 沒有 / 真實 / 的 / 自我 / 我 / 只有 / 消瘦 / 的 / 臉孔 / 所謂 / 軟弱 / 所謂 / 的 / 順從 / 一向 / 是 / 我 / 的 / 座右銘 / 而 / 我 / 沒有 / 那 / 海洋 / 的 / 寬闊 / 我 / 只要 / 熱情 / 的 / 撫摸 / 所謂 / 空洞 / 所謂 / 不安全感 / 是 / 我 / 的 / 墓誌銘 / 而 / 你 / 是否 / 和 / 我 / 一般 / 怯懦 / 是否 / 和 / 我 / 一般 / 矯作 / 和 / 我 / 一般 / 囉唆 / 而 / 你 / 是否 / 和 / 我 / 一般 / 退縮 / 是否 / 和 / 我 / 一般 / 肌迫 / 一般 / 地 / 困惑 / 我 / 沒有 / 力 / 我 / 沒有 / 滿腔 / 的 / 熱火 / 我 / 只有 / 滿肚 / 的 / 如果 / 所謂 / 勇氣 / 所謂 / 的 / 認同感 / 是 / 我 / 隨便說說 / 而 / 你 / 是否 / 和 / 我 / 一般 / 怯懦 / 是否 / 和 / 我 / 一般 / 矯作 / 是否 / 對 / 你 / 來說 / 只是 / 一場 / 遊戲 / 雖然 / 沒有 / 把握 / 而 / 你 / 是否 / 和 / 我 / 一般 / 退縮 / 是否 / 和 / 我 / 一般 / 肌迫 / 是否 / 對 / 你"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我們可以看到「座右銘」成功斷成「座右銘」了！「墓誌銘」也成功斷成「墓誌銘」了！果然切換成繁體中文詞庫還是有用的！"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 參考\n",
    "* [林志傑-如何使用 JIEBA 結巴中文分詞程式](http://blog.fukuball.com/ru-he-shi-yong-jieba-jie-ba-zhong-wen-fen-ci-cheng-shi/)\n",
    "* [结巴中文分词Github](https://github.com/fxsjy/jieba)\n",
    "* [中文斷詞：斷句不要悲劇](https://speakerdeck.com/fukuball/head-first-chinese-text-segmentation)\n",
    "* [結巴斷詞器與FastTag](http://blog.pulipuli.info/2017/11/fasttag-identify-part-of-speech-in.html)\n",
    "* [中文自然語言處理基礎](https://ithelp.ithome.com.tw/articles/10192043?sc=rss.qu)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
